package online.inote.naruto.api.access.validator;

import io.jsonwebtoken.*;
import online.inote.naruto.api.access.jwt.JwtHelper;
import online.inote.naruto.api.access.props.ApiAccessProperties;
import online.inote.naruto.cache.CacheSupport;
import online.inote.naruto.exception.token.TokenException;
import online.inote.naruto.utils.Assert;
import online.inote.naruto.utils.DateTimeUtils;
import online.inote.naruto.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Date;
import java.util.Objects;

/**
 * @description API访问权限校验器
 * @author XQF.Sui
 * @date 2021/07/30 14:01
 */
public class ApiAccessValidator {

  private static final Logger logger = LoggerFactory.getLogger(ApiAccessValidator.class);

  public static ApiAccessValidatorWorker builder() {
    return new ApiAccessValidatorWorker();
  }

  public static class ApiAccessValidatorWorker {

    private String token;
    private String method;

    public ApiAccessValidatorWorker token(String token) {
      Assert.notBlank(token, "Token不能为空");
      this.token = token;
      return this;
    }

    public ApiAccessValidatorWorker method(String method) {
      Assert.notBlank(method, "method不能为空");
      this.method = method;
      return this;
    }

    public void execute() {
      Jws<Claims> jws = JwtHelper.getClaims(this.token);
      Claims claims = jws.getBody();

      if (StringUtils.isBlank(claims.getIssuer()) || Objects.isNull(claims.getIssuedAt())) {
        throw new MalformedJwtException("Token解析失败");
      }

      if (logger.isDebugEnabled()) {
        logger.debug(
            "Token发行者为:[ {} ], 于[ {} ]生成, 授权给[ {} ]",
            claims.getIssuer(),
            DateTimeUtils.format(claims.getIssuedAt()),
            claims.getAudience());
      }

      isExpired(jws);

      if (ApiAccessProperties.props().getCache().getEnable()) {
        isForgery(jws);
        isPermission(jws);
      }
    }

    private void isPermission(Jws<Claims> jws) {
      if (!CacheSupport.isMember(getSystemAuthInterfaceKey(jws.getBody()), this.method)) {
        throw new TokenException("无权访问");
      }
    }

    private void isExpired(Jws<Claims> jws) {
      Claims claims = jws.getBody();
      Date expiration = claims.getExpiration();

      if (Objects.nonNull(expiration) && expiration.before(DateTimeUtils.getNow())) {
        throw new ExpiredJwtException(jws.getHeader(), claims, "Token已过期(签署)");
      }

      if (ApiAccessProperties.props().getCache().getEnable()) {
        boolean result = CacheSupport.isExist(getTokenKey(claims));

        if (!result) {
          throw new ExpiredJwtException(jws.getHeader(), claims, "Token已过期(缓存)");
        }
      }
    }

    private void isForgery(Jws<Claims> jws) {
      if (!StringUtils.equals(this.token, CacheSupport.get(getTokenKey(jws.getBody())))) {
        throw new IncorrectClaimException(jws.getHeader(), jws.getBody(), "伪造的Token信息");
      }
    }

    private String getTokenKey(Claims claims) {
      return cacheProps().initSystemTokenCacheKey(claims.getId());
    }

    private String getSystemAuthInterfaceKey(Claims claims) {
      return cacheProps().initSystemAuthInterfacePathCacheKey(claims.getId());
    }

    private ApiAccessProperties.Cache cacheProps() {
      return ApiAccessProperties.props().getCache();
    }
  }
}